#ifndef CRESOURCEMANAGER_H
#define CRESOURCEMANAGER_H

#include <string>
#include <stack>
#include <vector>
#include <stdint.h>
#include <limits.h>


template <typename T> class CResourceManager
{
private:
    uint32_t m_HandleCount;                                         // Used to make sure all handles have a unique value
    std::stack<uint32_t> m_Handles;                                 // Holds all the handles given out
    std::vector<T*> *m_List;                                        // Holds pointers to all of the resources
    void (*CreateResource)(T **resource, const unsigned int handle, const std::string& fileName, const std::string& path); // Application specific resource creation.

    CResourceManager(const CResourceManager& other)
    {

    }

    CResourceManager& operator=(const CResourceManager& rhs)
    {
        if (this == &rhs)
        {
            return *this;
        }

        return *this;
    }

protected:
public:
    CResourceManager(void (*CreateResourceFunction)(T **resource, const unsigned int handle, const std::string& fileName, const std::string& path) = NULL): m_HandleCount(0), CreateResource(CreateResourceFunction)
    {
        m_List = new std::vector<T*>;
    }

    virtual ~CResourceManager()
    {
        EmptyList();
        delete m_List;
    }

    int64_t Add(const std::string& fileName, const std::string& path = "./")
    {
        if (m_List == NULL || fileName.empty())
        {
            return -1;
        }

        T *resource = GetElement(fileName, path);

        if (resource != NULL)
        {
            resource->IncRef();
            return resource->GetHandle();
        }

        bool handleAvailable = !m_Handles.empty();
        uint32_t handle;

        if (handleAvailable)
        {
            handle = m_Handles.top();
            m_Handles.pop();
        }
        else
        {
            handle = m_HandleCount;
            m_HandleCount++;
        }

        if (CreateResource != NULL)
        {
            CreateResource(&resource, handle, path, fileName);
        }
        else
        {
            resource = new T(handle, fileName, path);
        }

        if (handleAvailable)
        {
            (*m_List)[handle] = resource;
        }
        else
        {
            m_List->push_back(resource);
        }

        return handle;
    }

    bool Remove(uint32_t handle)
    {
        if (m_List == NULL || handle >= m_List->size() || (*m_List)[handle] == NULL)
        {
            return false;
        }

        T *resource = (*m_List)[handle];
        resource->DecRef();

        if (resource->GetRefCount() == 0)
        {
            m_Handles.push(handle);
            delete resource;
            (*m_List)[handle] = NULL;
        }

        return true;
    }

    void EmptyList()
    {
        for (typename std::vector<T*>::iterator i = m_List->begin(); i != m_List->end(); ++i)
        {
            if (*i)
            {
                delete *i;
                *i = NULL;
            }
        }

        while (!m_Handles.empty())
        {
            m_Handles.pop();
        }

        m_List->clear();
        m_List->swap(*m_List);
    }

    // Getter Functions

    T* GetElement(const std::string& fileName, const std::string& path)
    {
        if (fileName.empty() || m_List == NULL || m_List->empty())
        {
            return NULL;
        }

        for (typename std::vector<T*>::iterator i = m_List->begin(); i != m_List->end(); ++i)
        {
            if ((*i) != NULL)
            {
                if ((*i)->GetFileName() == fileName)
                {
                    if ((*i)->GetPath() == path)
                    {
                        return (*i);
                    }
                }
            }
        }

        return NULL;
    }

    T* GetElement(uint32_t handle)
    {
        if (handle < m_List->size())
        {
            return (*m_List)[handle];
        }

        return NULL;
    }

    // Operation Overload Functions

    T* operator[](unsigned int handle)
    {
        return GetElement(handle);
    }
};

#endif // CRESOURCEMANAGER_H
